/* Copyright (C) 2011 Bjorn Regnell <bjorn.regnell@cs.lth.se> 
 * http://www.reqt.org
 * http://cs.lth.se/bjorn_regnell/
 * reqT version 1.0 compiled 2011-11-01 with Scala 2.9.1.final
 * 
 * The contents of this file are being to the GNU General Public License
 * Version 3, http://www.gnu.org/copyleft/gpl.html
 * You can redistribute and/or modify this file under the 
 * terms of the License, given that you include this header.
 * -----------------------------------------------------------------------
 * reqT is a requirements engieering Tool
 * 
 * reqT illustrates essential requirements engineering concepts
 * reqT allows you to manage requirements in a domain repository
 * reqT enables asynchronous feature tracking for evolving products
 * reqT supports creation, selection and planning of features 
 * reqT is a requirements language for the Scala interpreter
 * -----------------------------------------------------------------------     
 */
 
package reqT
import scala.collection.immutable.SortedMap

object all {
	val entities:List[Entity] = requirements ++ contextEntities
	val requirements:List[Requirement] = List(feature, function, data, quality, goal, task, usecase, userstory, scenario, interface)
	val contextEntities = List(product, release, stakeholder, actor, intro)
	val relations:List[Relation] = List(has, enables)
	val attributes:List[Attribute] = List(gist, spec, state, why, owner, example, prio, input, output, target, dir, purpose, frequency, trigger, critical, problem, solution, role, desire, benefit, contact, text, link, test, creation, revision)
	val concepts:List[Concept] = entities ++ requirements ++ relations  
	val levels:List[Level] = List(created, specified, planned, implemented, tested, released, failed, postponed, dropped)
	val directions:List[Direction] = List(from, to, bi)
	val attributeValues = levels ++ directions
	val metamodel:List[Metamodel] = concepts ++ attributes ++ attributeValues
}

abstract class Metamodel
abstract class Concept extends Metamodel with ConceptSemantics
case object Concept extends Concept 

abstract class Entity extends Concept with EntitySemantics 
case object Entity extends Entity 
case object product extends Entity with EntityMaker //with ProductSemantics //gist spec
case object release extends Entity with EntityMaker //with ReleaseSemantics //gist spec date
case object stakeholder extends Entity with EntityMaker //with StakeholderSemantics //gist contact
case object actor extends Entity with EntityMaker //with ActorSemantics 
case object intro extends Entity with EntityMaker //used for intro chapter generation in latex

abstract class Requirement extends Entity with RequirementsSemantics
case object Requirement extends Requirement
case object feature extends Requirement with EntityMaker with FeatureSemantics 
case object function extends Requirement with EntityMaker with FunctionSemantics
case object data extends Requirement with EntityMaker //with etc
case object quality extends Requirement with EntityMaker
case object goal extends Requirement with EntityMaker
case object task extends Requirement with EntityMaker
case object subtask extends Requirement with EntityMaker
case object usecase extends Requirement with EntityMaker
case object userstory extends Requirement with EntityMaker
case object scenario extends Requirement with EntityMaker
case object interface extends Requirement with EntityMaker

abstract class Relation extends Concept 
case object Relation extends Relation
case object has extends Relation with RelationMaker
case object enables extends Relation with RelationMaker

abstract class Attribute extends Metamodel  

case object gist extends Attribute // with StringSemantics
case object spec extends Attribute // with StringSemantics 
case object state extends Attribute // with StateSemantics 
case object why extends Attribute  // with StringSemantics
case object owner extends Attribute  // with StringSemantics
case object example extends Attribute // with StringSemantics
case object prio extends Attribute // with IntSemantics 
case object input extends Attribute // with StringSemantics //function
case object output extends Attribute // with StringSemantics //function
case object target extends Attribute // with StringSemantics //quality
case object dir extends Attribute  // with StringSemantics //interface
case object purpose extends Attribute //task
case object trigger extends Attribute //task
case object frequency extends Attribute //task
case object critical extends Attribute //task
case object problem extends Attribute //subtask
case object solution extends Attribute //subtask
case object contact extends Attribute // with StringSemantics
case object being extends Attribute //actor attribute
case object role extends Attribute  // with StringSemantics //userstory
case object desire extends Attribute  // with StringSemantics //userstory
case object benefit extends Attribute  // with StringSemantics //userstory
case object text extends Attribute  // with StringSemantics
case object link extends Attribute // with LinkSemantics
case object test extends Attribute 
case object creation extends Attribute
case object revision extends Attribute

abstract class AttributeValue extends Metamodel
abstract class Level extends AttributeValue { val up:Level; val down:Level  }
case object created extends Level { override val (up,down)=(specified,dropped) }
case object specified extends Level { override val (up,down)=(planned,dropped) } 
case object planned extends Level { override val (up,down)=(implemented,postponed) }
case object implemented extends Level { override val (up,down)=(tested,failed) }
case object tested extends Level { override val (up,down)=(released,failed) }
case object released extends Level { override val (up,down)=(released,dropped) }
case object failed extends Level { override val (up,down)=(implemented,dropped) }
case object postponed extends Level { override val (up,down)=(planned,postponed) }
case object dropped extends Level { override val (up,down)=(created,dropped) }
case object noLevel extends Level { override val (up,down)=(noLevel,noLevel) }

abstract class Direction extends AttributeValue
case object from extends Direction
case object to extends Direction
case object bi extends Direction
case object noDirection extends Direction 

abstract class ActorType extends AttributeValue
case object human extends ActorType
case object machine extends ActorType
case object noBeing extends ActorType 

abstract class Key( val concept:Concept) extends Metamodel { def toScala:String }
case class Ent(ent:Entity, id:String) extends Key(ent) { 
	def has(member:Ent*):(Rel,RelTo) = reqT.has(this,member.toSet)
	def has:Rel = reqT.has(this)
	def enables(enabler:Ent*):(Rel,RelTo) = reqT.enables(this,enabler.toSet)
	def enables:Rel = reqT.enables(this)
	override def toScala:String = "" + ent + "(\"" + id + "\")" 
}
case class Rel(entKey : Ent, rel:Relation) extends Key(rel) {
	override def toScala:String = ""  + entKey.toScala + " " + rel
}

abstract class Elements extends Metamodel { def toScala:String }
case class Attr(elms:Map[Attribute,Any]) extends Elements {
	def has(a:Attribute):Boolean = elms.isDefinedAt(a)
	def getStringOrElse(attrib:Attribute, noVal:String):String = elms.get(attrib) match { 
		case Some(x:String) => x
		case Some(x) =>  warn(attrib + " -> " + x + " not of type String"); noVal
		case None => warn(attrib + " is not in Attr"); noVal
	}
	def getIntOrElse(attrib:Attribute, noVal:Int):Int = elms.get(attrib) match { 
		case Some(x:Int) => x
		case Some(x) =>  warn(attrib + " -> " + x + " not of type Level"); noVal
		case None => warn(attrib + " is not in Attr"); noVal
	}	
	def getLevelOrElse(attrib:Attribute, noVal:Level):Level = elms.get(attrib) match { 
		case Some(x:Level) => x
		case Some(x) =>  warn(attrib + " -> " + x + " not of type Level"); noVal
		case None => warn(attrib + " is not in Attr"); noVal
	}
	def getDirectionOrElse(attrib:Attribute, noVal:Direction):Direction = elms.get(attrib) match { 
		case Some(x:Direction) => x
		case Some(x) =>  warn(attrib + " -> " + x + " not of type Level"); noVal
		case None => warn(attrib + " is not in Attr"); noVal
	}
	def getRelToOrElse(attrib:Attribute, noVal:RelTo):RelTo = elms.get(attrib) match { 
		case Some(x:RelTo) => x
		case Some(x) =>  warn(attrib + " -> " + x + " not of type Level"); noVal
		case None => warn(attrib + " is not in Attr"); noVal
	}
	def getTimeOrElse(attrib:Attribute, noVal:Time):Time = elms.get(attrib) match { 
		case Some(x:Time) => x
		case Some(x) =>  warn(attrib + " -> " + x + " not of type Time"); noVal
		case None => warn(attrib + " is not in Attr"); noVal
	}
	def getActorTypeOrElse(attrib:Attribute, noVal:ActorType):ActorType = elms.get(attrib) match { 
		case Some(x:ActorType) => x
		case Some(x) =>  warn(attrib + " -> " + x + " not of type ActorType"); noVal
		case None => warn(attrib + " is not in Attr"); noVal
	}	
	def gist:String = getStringOrElse(reqT.gist, "")
	def state:Level = getLevelOrElse(reqT.state, noLevel)
	def spec:String = getStringOrElse(reqT.spec, "") 
	def why:String = getStringOrElse(reqT.why, "") 
	def owner:String = getStringOrElse(reqT.owner, "") 
	def example:String = getStringOrElse(reqT.example, "") 
	def prio:Int = getIntOrElse(reqT.prio, 0) 
	def input:String = getStringOrElse(reqT.input, "") 
	def output:String = getStringOrElse(reqT.output, "") 
	def target:String = getStringOrElse(reqT.target, "") 
	def dir:Direction = getDirectionOrElse(reqT.dir, noDirection)
	def purpose:String = getStringOrElse(reqT.purpose, "") 
	def frequency:String = getStringOrElse(reqT.frequency, "") 
	def trigger:String = getStringOrElse(reqT.trigger, "") 
	def critical:String = getStringOrElse(reqT.critical, "") 
	def problem:String = getStringOrElse(reqT.problem, "") 
	def solution:String = getStringOrElse(reqT.solution, "") 
	def contact:String = getStringOrElse(reqT.contact, "") 
	def being:ActorType = getActorTypeOrElse(reqT.being, noBeing) 
	def role:String = getStringOrElse(reqT.role, "") 
	def desire:String = getStringOrElse(reqT.desire, "") 
	def benefit:String = getStringOrElse(reqT.benefit, "") 
	def text:String = getStringOrElse(reqT.text, "") 
	def link:RelTo = getRelToOrElse(reqT.link, RelTo()) 
	def test:String = getStringOrElse(reqT.test, "")
	def creation:Time = getTimeOrElse(reqT.creation, Time.noTime)
	def revision:Time = getTimeOrElse(reqT.revision, Time.noTime)
	override def toString = 
		"Attr(" +  elms.map( e => "" + e._1 + 
		" -> " + e._2).mkString(", ") + ")"
	override def toScala  = 
		"Attr" +  elms.map( e => "" + e._1 + " -> " + 
			export.valueToString(e._2)).mkString("(", ",", ")")
}
case object Attr extends Elements {
	def apply(e:(Attribute, Any)*):Attr = Attr(e.toMap)
	override def toScala = "Attr"
} 
case class RelTo(elms:Set[Ent]) extends Elements {
	override def toString = 
		"RelTo(" +  elms.map( _.toString ).mkString(",") + ")"
	override def toScala = 
		"RelTo(" +  elms.map( _.toScala ).mkString(",") + ")"
}
case object RelTo extends Elements {
	def apply(e:Ent*):RelTo = RelTo(e.toSet)
	override def toScala = "RelTo"
} 
//case object noElement extends Elements { override def toScala = "()"}

case class Domain(db:SortedMap[Key,Elements]) extends Metamodel { 
	def inspect {
		println("Domain(")
		subDomain(Entity).foreach(x => println("  " + x._1.toScala + " -> " + 
			{ if (x._2 == Attr()) "Attr()" else "Attr(...)" } ) )
		subDomain(Relation).foreach(x => println("  " + x._1.toScala + " -> " + 
			{ if (x._2 == RelTo()) "RelTo()" else "RelTo(...)" } )) 
		println(")")
	}
	def ? = inspect
	def inspectAll {
		println(this toScala)
		//println("Domain(")
		//subDomain(Entity).foreach(x => println("  " + x._1.toScala + " -> " + x._2.toScala) )
		//subDomain(Relation).foreach(x => println("  " + x._1.toScala + " -> " + x._2.toScala)) 
		//println(")")
	}
	def ?? = inspectAll
	def test:List[(Key,scala.tools.nsc.interpreter.IR.Result)]= {
		val repl = new scala.tools.nsc.interpreter.IMain 
		( this / reqT.test ) map (x => 
			x._2 match {
				case a:Attr =>  if (a has reqT.test) { 
						repl.reset
						(x._1, repl.interpret(a.test))
					}
					else (x._1, scala.tools.nsc.interpreter.IR.Error)
				case _ => (x._1, scala.tools.nsc.interpreter.IR.Error)
			}
		) toList
	}
	def get(k:Key) = db.get(k) 
	def inspect(e:Ent):Attr = {
		val elm = db.getOrElse(e, Attr())
		elm match { case x:Attr => x; case _ => Attr() } 
	}
	def ?(e:Ent):Attr = inspect(e) 
	def inspect(r:Rel):RelTo = {
		val elm = db.getOrElse(r, RelTo())
		elm match { case x:RelTo => x; case _ => RelTo() } 
	}
	def ?(r:Rel):RelTo = inspect(r) 
	def inspect(a:Attribute) {  
		(this / a) foreach (x => x._2 match { case attr: Attr => println("  " + x._1.toScala + " " + a + " -> " + attr.elms(a)) case _ => } )
	}  
	def ?(a:Attribute) {inspect(a)}
	def inspectIds = ids foreach println _
	def ids:List[String] =  subDomain(Entity).db.map(x => x._1 match { 
		case e: Ent => e.id; case _ => "" }).toList // add also ids from relTo???
	def foreach(f:((Key,Elements)) => Unit) = db.foreach(f)
	def count(p:((Key, Elements)) => Boolean):Int = db.count(p)
	def filter(p:((Key, Elements)) => Boolean):Domain = Domain(db.filter(p))
	def map[A](f:((Key,Elements)) => A) = db.map(f) 
	def length = count(_ => true )
	def ?#():Int = length 
	def keys = db.keys
	def entKeys = subDomain(Entity).db.map(x => x._1 match { case e: Ent => e } ).toSet 
	def relKeys = subDomain(Relation).db.map(x => x._1 match { case r: Rel => r } ).toSet 
	def keySet = db.keySet
	def contains(k:Key) = db.contains(k)
	def isDefinedAt(k:Key) = db.isDefinedAt(k)
	def isEmpty:Boolean = (this == Domain())
	def has(k:Key):Boolean = isDefinedAt(k:Key)
	def elms(k:Ent):Map[Attribute,Any] = if (!has(k)) Map()
		else this.db(k) match {
			case a:Attr => a.elms
			case _ => {warn("Ent is not mapped to type Attr."); Map() }
		}
	def elms(k:Rel):Set[Ent] = if (!has(k))Set() 
		else this.db(k) match {
			case r:RelTo => r.elms
			case _ => {warn("Rel is not mapped to type RelTo."); Set() }
		}
	def head:(Key,Elements) = (this.db.head._1,this.db.head._2)
	def tail:Domain = Domain(this.db.tail)
	def add(m:(Key,Elements)):Domain = m._1 match {
		case e : Ent => //only add if not existing
			if (has(e)) {
				warn("Key collision. "+e+" not added. Use *= for update.")
				this 
			}
			else Domain(this.db + (e -> m._2))
		case r : Rel => //add relation, merge if existing
			if (has(r)) {
				val newElms : Set[Ent] = m._2 match {
					case RelTo(elms) => elms
					case _ => {warn("Rel is not mapped to type RelTo."); Set() }
				}
				val oldElms : Set[Ent] = this.elms(r)
				this - r + (r -> RelTo(oldElms ++ newElms))
			} else Domain(this.db + (r -> m._2))
	} 
	def update(m:(Key,Elements)):Domain = m._1 match { 
		case e:Ent => //add Ent, overwrite attr elms with set add ++ if existing 
			if (!has(e)) {warn("Key absent. "+e+" not updated!"); this }
			else m._2 match { 
				case a:Attr => {
					val (key, newElms, oldElms) = (e, a.elms, this.elms(e))
					this - key + (key -> Attr(oldElms ++ newElms ++ Map(revision -> Time.now)))
				}
				case _ => {warn("Ent has no Attr");this}
			}
		case r:Rel => //add relation, merge if existing 
			this + m
	}
	def +(m:(Key,Elements)):Domain = add(m)
	def +(d:Domain):Domain = if (d.isEmpty) this else (this + d.head) + d.tail //tail recursive
	def +(k:Ent):Domain = Domain(this.db + k.ent.construct(k , Attr()))
	def -(m:Key):Domain = if (!has(m)) {warn("Key absent. "+m+" not deleted!"); this } else Domain(this.db - m)
	def -(r:(Rel,RelTo)):Domain = { // remove only the relations in r._2
		val relTo = this ? r._1 
		val removed = RelTo(relTo.elms -- r._2.elms) 
		this - r._1 + (r._1, removed)
	} 
	def -(d:Domain):Domain = Domain(db -- d.keys)
	def *(m:(Key,Elements)):Domain = update(m) 
	def *(d:Domain):Domain = if (d.isEmpty) this else (this * d.head) * d.tail //tail recursive
	def subDomain(c:Concept):Domain = c match {
		case Requirement => allRequirements
		case Entity => allEntities
		case Relation => allRelations
		case Concept => allConcepts
		case _ => Domain(db.filter(x => (x._1.concept == c)))
	}
	def subDomain(a:Attribute):Domain = Domain(db.filter( x => x._2 match { 
		case attr: Attr => (attr.elms get a) != None ; case _ => false } ))
	def subDomain(m:(Attribute,Any)):Domain = Domain(db.filter( x => x._2 match { 
		case a: Attr => (a.elms get m._1) == Some(m._2) ; case _ => false } ))
	def find(s:String):Domain = Domain(db.filter( x => x.toString contains s ))
	def ?(s:String):Domain = find(s)
	def subDomain(entId:String):Domain = Domain(db.filter( x => x._1 match { 
			case e:Ent => e.id == entId 
			case r:Rel => (r.entKey.id == entId) || (x._2 match {
				case rt:RelTo => (false /: rt.elms.map(x => x.id == entId))( _ || _ )
				case _ => false
			})
		} ))
	def subDomain(e:Ent) = Domain(db.filter( x => (x._1 == e) || 
		(x._1 match { case r:Rel => r.entKey == e case _ => false } ) ||
		(x._2 match { case rt: RelTo => rt.elms.contains(e)  ; case _ => false } )))
	def subDomain(r:Rel) = Domain(db.filter( x => (x._1 == r) ))
	def /(c:Concept):Domain = subDomain(c) 
	def /(a:Attribute):Domain = subDomain(a)
	def /(m:(Attribute,Any)):Domain = subDomain(m)
	def /(s:String):Domain = subDomain(s)
	def /(e:Ent)  = subDomain(e)
	def /(r:Rel) = subDomain(r)
	def allConcepts:Domain = Domain(db.filter( x => x._1 match { 
		case e: Ent => e.ent.isInstanceOf[Concept]		
		case r: Rel => r.rel.isInstanceOf[Concept]		
		case _ => false } ))
	def allRequirements:Domain = Domain(db.filter( x => x._1 match { 
		case a: Ent => a.ent.isInstanceOf[Requirement]; case _ => false } ))
	def allRelations:Domain = Domain(db.filter( x => x._1 match { 
		case a: Rel => true ; case _ => false } ))
	def allEntities:Domain = Domain(db.filter( x => x._1 match { 
		case a: Ent => true ; case _ => false } ))
	def complement(x:Concept):Domain = this - this / x
	def complement(x:Attribute):Domain = this - this / x
	def complement(x:(Attribute,Any)):Domain = this - this / x
	def complement(x:String):Domain = this - this / x
	def complement(x:Ent):Domain = this - this / x
	def complement(x:Rel):Domain = this - this / x
	def \(x:Concept) = complement(x)
	def \(x:Attribute) = complement(x)
	def \(x:(Attribute,Any)) = complement(x)
	def \(x:String) = complement(x)
	def \(x:Ent) = complement(x)
	def \(x:Rel) = complement(x)
	def split(x:Concept):(Domain,Domain) = ( this / x, this \ x)
	def split(x:Attribute):(Domain,Domain) = ( this / x, this \ x)
	def split(x:(Attribute,Any)):(Domain,Domain) = ( this / x, this \ x)
	def split(x:String):(Domain,Domain) = ( this / x, this \ x)
	def split(x:Ent):(Domain,Domain) = ( this / x, this \ x)
	def split(x:Rel):(Domain,Domain) = ( this / x, this \ x)
	def |(x:Concept) = split(x)
	def |(x:Attribute) = split(x)
	def |(x:(Attribute,Any)) = split(x)
	def |(x:String) = split(x)
	def |(x:Ent) = split(x)
	def |(x:Rel) = split(x)
	def up(e:Ent):Domain = { 
		val s = ?(e).state 
		if (s == noLevel) this else this.*((e, Attr(state -> s.up))) 
	}  
	def ^(e:Ent) = up(e)
	def down(e:Ent):Domain = { 
		val s = ?(e).state 
		if (s == noLevel) this else this.*((e, Attr(state -> s.down))) 
	}  
	def !^(e:Ent) = down(e)
	def toScala = export.domToScala( this )
	def toLatex = export.domToLatex( this )
	def save(fName : String) {export.saveAsScala(this , fName)}
	def latex(fName : String) {export.saveAsLatex(this , fName)}
	override def toString = "Domain" + db.map(x => x._1.toScala + 
		{ if (x._2 == Attr()) "" else "->..."} ).mkString("(", ",", ")")  
}

case object Domain {
	val keyOrdering = Ordering.fromLessThan[Key](
		(k1:Key, k2:Key) => k1.toScala < k2.toScala) 
	def apply(m:(Key,Elements)*):Domain = {
		val sm = SortedMap.empty[Key,Elements](keyOrdering)
		//This does not work: 
		// Domain(sm ++ m.map( o => o._1 -> o._2).toMap)
		// Have to add one at a time; otherwise multiple relations are overwritten
		// this is because the + operation for Domain in case of relations 
		// is not overwriting but appending (see Domain.add)
		var d = Domain(sm)
		for (ke <- m) d += ke
		d
	} 
	def apply(e:Ent):Domain = Domain() + e
}

trait EntityMaker extends Entity {
	def apply(id:String):Ent = Ent(this,id)
	def apply(id:String, attrVals:(Attribute,Any)*):(Ent,Attr) =
		construct(Ent(this,id),Attr(attrVals.toMap))
	def apply(id:String, a:Attr):(Ent,Attr) =
		construct(Ent(this,id),a)
}

trait RelationMaker extends Relation {
	def apply(ent:Ent):Rel = Rel(ent, this) 
	def apply(ent:Ent, rels:Ent*):(Key,Elements) = {
		(Rel(ent, this),RelTo(rels.toSet)) //TODO construct in RelationSemantics
	}
	def apply(ent:Ent, rels:Set[Ent]) = 
		(Rel(ent, this),RelTo(rels)) //TODO construct in RelationSemantics
}

abstract trait ConceptSemantics extends Metamodel { 
	def checkSemantics:Boolean = true //TODO 
	var warnings = Seq[String]()   //Q2C when to use a List and when to use a Seq??
}
abstract trait EntitySemantics extends ConceptSemantics {
	def attrSeq = Seq[Attribute](gist)
	def construct(e:Ent, a:Attr):(Ent,Attr) = (e,Attr(a.elms ++ 
		( if (a has creation) Map() else Map(creation -> Time.now)) ++ 
		( if (a has revision) Map() else Map(revision -> Time.now) ) ) )
}
trait RequirementsSemantics extends EntitySemantics {
	override def attrSeq = super.attrSeq ++ Seq(spec, why, owner)
} 
trait FeatureSemantics extends RequirementsSemantics {
	override def attrSeq = super.attrSeq ++ Seq(state, prio)
	override def construct(e:Ent, a:Attr):(Ent,Attr) = 
		if (a has state) super.construct(e, a)
		else super.construct(e, Attr(a.elms ++ Map(state -> created) ) )
}
trait FunctionSemantics extends RequirementsSemantics {
	override def attrSeq = super.attrSeq ++ Seq(input, output)
} 

trait StringSemantics extends ConceptSemantics {
	//TODO
}

//package object reqT {} //Q2C why is this clashing with reqT.gist etc when compiling

trait Utils {
	def firstCap(s:String):String = if (s == "") "" else s.head.toUpper + s.tail
	def quoteIfString(a:Any):String = a match {
		case s:String => "\"" + s + "\""
		case _ => a.toString
	}
	def escapeSeq(s:String):String = (for (c <- s) yield c match {
		case '\b' => '\\'+"b"
		case '\t' => '\\'+"t"
		case '\n' => '\\'+"n"
		case '\f' => '\\'+"f"
		case '\r' => '\\'+"r"
		case '\"' => ""+'\\'+'\"'
		case '\'' => ""+'\\'+ """'"""
		case '\\' => ""+'\\'+'\\'	
		case _ => c.toString
	}).mkString("")
	def charToUnicodeSeq(c:Char):String = if (c >= ' ') c.toString else {
		val h = Integer.toHexString(c)
		val zeroes = (for (i <- 1 to (4-h.length)) yield "0").mkString("")
		"\\u" + zeroes + h
	}
	def unicodeSeq(s:String):String = 
		(for (c <- s) yield charToUnicodeSeq(c)).mkString("")
	def escape(s:String):String = if (s == "()") s else
		"" + '\"' + unicodeSeq(escapeSeq(s)) + '\"'
	//def lineBreaks(s:String):String =  
	//	if (!s.contains("//")) s else 
	//		"(" + s.replaceAll("//","\" +\n      \"//") + "\n    )"
	def valueToString(v:Any):String = v match {
		case s:String =>  escape(s)    //lineBreaks(escape(s)) //removed as collide with latex
		case _ => v.toString
	}
	def valueToRawString(v:Any) :String = v match {
		case s:String =>  "\"\"\"" + s + "\"\"\""
		case _ => v.toString
	}
	def scalaSuffix(s:String):String = if (!s.contains(".")) s + ".scala" else s
	def latexSuffix(s:String):String = if (!s.contains(".")) s + ".tex" else s
	def txtSuffix(s:String):String = if (!s.contains(".")) s + ".txt" else s
	def varPrefix(s:String):String = if (s.contains(".")) "" else  "var " + s + " = "
}

object export extends Utils {
	def keyToScala(k:Key):String = k match {
		case ek : Ent => "  " + ek.ent + "(\"" + ek.id +"\""
		case rk : Rel => 
			"  " + rk.entKey.ent + "(\"" + rk.entKey.id +"\") " + rk.rel + " ("
	}
	def elementsToScala(e:Elements):String = e match {
		case av : Attr => if (av == Attr()) "Attr()"
			else { for ((a,v) <- av.elms) yield 
				( "\n    " + a + " -> " +  
					(if (a != test) valueToString(v) else valueToRawString(v)) 
				)
			}.mkString(",")
		case kr : RelTo => { for (e <- kr.elms) yield 
			("" + e.ent + "(\"" + e.id +"\")" ) 
		}.mkString(",")
	}
	def domToScala(d:Domain):String = "Domain(" + {
		(for ((k,e) <- d.db) yield k match {
			case ek : Ent =>	{ 	"\n" + 
				keyToScala(ek) + ", " + elementsToScala(e) + "\n  )"
			}
			case rk : Rel => { "\n" + 
				keyToScala(rk) + elementsToScala(e) + ")"
			} 
		}
		).mkString(", ")
	} + "\n)"
	def saveAsScala(d:Domain, fName : String) = {
		var outFile = new java.io.FileOutputStream(scalaSuffix(fName))
		var outStream = new java.io.PrintStream(outFile)
		outStream.println(varPrefix(fName) + d.toScala)
		outStream.close
		println("Saved to file: "+scalaSuffix(fName)) 
	} 
	def saveAsLatex(d:Domain, fName : String) = {
		var outFile = new java.io.FileOutputStream(latexSuffix(fName))
		var outStream = new java.io.PrintStream(outFile)
		outStream.println(d.toLatex)
		outStream.close
		println("Saved to file: "+latexSuffix(fName)) 
	} 
	def keyElementsToLatex(k:Key, e:Elements):String = e match {
		case av : Attr => if (av == Attr()) "No attributes defined."
			else "\n\\subsection{Attributes}\n\\begin{description}" + 
			{ for ((a,v) <- av.elms) yield 
				( "\n\\item["  + a + "]: " +  v.toString )
			}.mkString + "\n\\end{description}" 
		case kr : RelTo => if (kr == RelTo()) ""
			else "\n\\subsection{Relations}" + 
			{ for (e <- kr.elms) yield 
				("" + k.concept + e.ent + "(\"" + e.id +"\")" ) 
			}.mkString(",")
	}
	val latexPreamble = 
		"\\documentclass[11pt,a4paper,oneside]{report}\n"+
		"\n\\usepackage{longtable}\n" + 
		"\n\\usepackage{tikz}\n\\usetikzlibrary{shapes, arrows, positioning,fit,trees}" +
		"\n\\usepackage{hyperref}" + 
		"\n\\hypersetup{colorlinks=true, linkcolor=blue, urlcolor=blue}" +
		"\n\\usepackage{framed}" +
		"\n\\begin{document}\n"
	val latexEnding = "\n\\end{document}"
	def domToLatex(d:Domain):String = {
		warn.off
		val (dIntro, dRest1) = d | intro
		val (dReqs, dRest2) = dRest1 | Requirement
		val title = if (dIntro ? intro("title") has text)
			dIntro ? intro("title") text else "Requriement Specification"
		val name = if (dIntro ? intro("name") has text) 
			dIntro ? intro("name") text else "Untitled"
		val author = if (dIntro ? intro("author") has text) 
			dIntro ? intro("author") text else "Generated with \\texttt{reqT}"
		val introduction:String = (dIntro - intro("name") - intro("title") - intro("author")).map(
			x => x._2 match {
				case a:Attr => "\n\\section{"+
					(x._1 match { case e:Ent => firstCap(e.id); case _ => "Unkown" } ) + 
					"}\n" + (a text) + "\n"
				case _ => ""
			} 
		).mkString("\n")
		def verbatim(s:String) = "\n\\begin{verbatim}\n" + s + "\n\\end{verbatim}\n"
		def bold(s:String) = "{\\bf " + s + "}"
		def italics(s:String) = "{\\it " + s + "}"
		def setLabel(e:Ent) = "\\label{" + e.ent + e.id + "}\n"
		def refLabel(e:Ent, t:String) = "\\hyperref[" + e.ent + e.id + "]{" + t + "}"		
		def entToLatex(e:Ent) = firstCap(e.ent.toString) + " \\emph{" + refLabel(e, e.id)  + "}"
		def entToLatexTable(e:Ent) = firstCap(e.ent.toString) + " & \\emph{" + refLabel(e, e.id)  + "}"
		def featureTableBegin(cap:String) = "\\begin{center}\n\\begin{longtable}{l|p{3cm}|p{4cm}|p{2.1cm}}\n" + 
			"\\caption{"+cap+"} \\label{featuretab} \\\\ \n" +
			"\\emph{Entity} & \\emph{id} & \\emph{gist} & \\emph{state} \\\\ \n \\hline\n \\endhead\n"	
		def featureTable(dom:Domain):String = "" + (for ((k,e) <- dom.db) yield k match { 
			case ek : Ent =>	{
				entToLatexTable(ek) + (e match { 
						case a:Attr => " & " + (a gist) + " & " + (if (a has state) (a state) else "")
						case _ => " & & "
					}) 
			}
			case _ => ""
		} ).mkString("", "\\\\ \\hline \n", "\n") 
		def entityTableBegin(cap:String) = "\\begin{center}\n\\begin{longtable}{l|p{3cm}|p{6.1cm}}\n" + 
			"\\caption{"+cap+"} \\label{entitytab} \\\\ \n" +
			"\\emph{Entity} & \\emph{id} & \\emph{gist} \\\\ \n \\hline\n \\endhead\n"	
		def entityTable(dom:Domain):String = "" + (for ((k,e) <- dom.db) yield k match { 
			case ek : Ent =>	{
				entToLatexTable(ek) + (e match { 
						case a:Attr => " & " + (a gist)
						case _ => " & & "
					}) 
			}
			case _ => ""
		} ).mkString("", "\\\\ \\hline \n", "\n") 		
		def relationTableBegin(cap:String) = "\\begin{center}\n\\begin{longtable}{l|l|p{2cm}|p{6.1cm}}\n" + 
			"\\caption{"+cap+"} \\label{relationtab} \\\\ \n" +
			"\\emph{Entity} & \\emph{id} & \\emph{relation} & \\emph{related to} \\\\ \n \\hline\n \\endhead\n"	
		def relationTable(dom:Domain):String = "" + (for ((k,e) <- (dom / Relation).db) yield k match { 
			case rk : Rel =>	{
				firstCap(rk.entKey.ent.toString) + " & \\emph{" + refLabel(rk.entKey, rk.entKey.id)  + "}" + (e match { 
						case rt:RelTo => " & " + rk.rel + "& " + rt.elms.map( x => entToLatex(x) ).mkString(" \\\\ \n & & & ")
						case _ => " & & & "
					}) 
			}
			case _ => ""
		} ).mkString("", "\\\\ \\hline \n", "\n") 
		val tableEnd = "\n\\end{longtable}\n\\end{center}\n"
		val overview = 	featureTableBegin("Overview of features.") + featureTable((dRest1 / feature) \ Relation) + tableEnd +
						entityTableBegin("Overview of other entities.") + entityTable((dRest1 \ feature) \ Relation) + tableEnd +
						relationTableBegin("Overview of relations.") + relationTable(dRest1 / Relation) + tableEnd
		def contextChapter = entitySection(dRest2 \ Relation)
		def contextDiagramBegin = 
			"\\begin{figure}\n\\begin{center}" +
			"\\tikzstyle iFrom=[->,shorten >=0pt,shorten <=0pt,>=stealth,gray,thick]\n" +
			"\\tikzstyle iTo=[<-,shorten >=0pt,shorten <=0pt,>=stealth,gray,thick]\n" +
			"\\tikzstyle iBi=[<->,shorten >=0pt,shorten <=0pt,>=stealth,gray,thick]\n" +
			"\\tikzstyle iNoDir=[-,shorten >=0pt,shorten <=0pt,>=stealth,gray,thick]\n" +
			"\\tikzstyle machine=[rectangle, draw=black,thin]\n" +
			"\\tikzstyle linestyle=[gray!40, text=black]\n" +
			"\\begin{tikzpicture}[align=flush center,font=\\footnotesize, grow cyclic,\n" +
			"level 1/.style={level distance=5cm,sibling angle=30},\n" +
			"level 2/.style={text width=2cm,  level distance=3cm}]\n"
		def contextDiagramEnd(p:Ent) = 
			"\\end{tikzpicture}\n\\end{center}" + 
			"\\caption{Context diagram for " + p.ent + " " + refLabel(p, p.id) + ".} \\label{context"+ p.ent + p.id + "} " + 
			"\n\\end{figure}\n"
		def contextDiagram(p:Ent):String = {
			def strawman(actorId:String) = "\n\\draw (actor"+actorId+") ++(-1.5mm,2mm) -- +(1mm,2mm) -- +(2mm,0mm) " +
				"++(1mm,2mm) -- +(0mm,2mm) +(0mm,3.3mm) circle (1.2mm) ++(-1.5mm,1mm) -- +(3mm,0mm);"
			val pHas = (d / p) / has  // all has relations of Ent p
			val pInterfaces:List[String] = pHas.elms(p.has).filter( e => e.ent == interface).map(e => e.id).toList
			val pRelTo:List[Elements] = pInterfaces.map(x => d get interface(x).has getOrElse(RelTo(actor("unknown "+x)))) 
			val pActorEntSets:List[Set[Ent]] = pRelTo map(x => x match{ case rt:RelTo => rt.elms.filter(x => x.ent == actor); case _ => Set[Ent]()})
			val pActors:List[String] = pActorEntSets map (x => x map (e => e.id) mkString(",")) //special case: many actors in same interface
			val pDirections:List[Direction] = pInterfaces.map(x => (d.get(interface(x)).getOrElse(Attr()) match {case a:Attr => a; case _ => Attr()}) dir )
			val pDirStyle:List[String] = pDirections.map(x => if (x == to) "iTo" else if (x == from) "iFrom" else if (x == bi) "iBi" else "iNoDir")
			val pActorTypes:List[ActorType] = pActorEntSets map(x => if (x.size == 1 && (d ? x.head being) == human) human else machine) 
			val pActorsAndTypes = pActors zip pActorTypes
			val pZip = pInterfaces zip pActors zip pDirStyle map (x => (x._1._1, x._1._2, x._2))
			if (pZip.isEmpty) "" else {
			"\n\\subsection{Context diagram}\n" + contextDiagramBegin +
			"\\node[rectangle, draw=black, line width=1.8pt] (product" + p.id + ") {" + p.id + "}\n" + //draw product
				pActorsAndTypes.map( a => { //draw actors
					"child[linestyle]{node" + ( if (a._2 == machine) "[machine]" else "" )+ " (actor"+a._1+") {"+a._1+"}}"
				} ).mkString("", "\n", ";") + 
				pZip.map( tuple => { //draw interfaces
					"\n\\draw["+tuple._3+"] (product"+p.id+") to node[black,sloped,above]{"+tuple._1+"} (actor"+tuple._2+");\n"
				} ).mkString("\n") + 
				pActorsAndTypes.map( a => if (a._2 == human) strawman(a._1) else ""  ).mkString("\n") + 
				contextDiagramEnd(p)
			}
		}
		def userStoryFrame(us:Ent):String = {
			val usRole = if (d ? us has role) (d ? us role) else "$<$role$>$"
			val usDesire = if (d ? us has desire) (d ? us desire) else "$<$desire$>$"
			val usBenefit = if (d ? us has benefit) (d ? us benefit) else "$<$benefit$>$"
			"\n\\begin{framed}\n" + 
			"As a " + usRole + ", I want to " + usDesire + " so that " + usBenefit + "." +
			"\n\\end{framed}\n" 
		}
		def taskTable(t:Ent):String = {
			val tAttr = d ? t
			val tHas = (d / t) / has  // all has relations of Ent p
			val subtasks = tHas.elms(t.has).filter( e => e.ent == subtask).map(e => e).toList
			"\n\\begin{framed}\n\\begin{longtable}{l p{7cm}}" + 
			"\n\\caption{{\\bf Task} " + italics(t.id) + ".} \\label{task"+t.id+"} \\\\" + 
			"\n{\\it Purpose} & " + (tAttr purpose) + "\\\\" +
			"\n{\\it Trigger} & " + (tAttr trigger) + "\\\\" +
			"\n{\\it Frequency} & " + (tAttr frequency) + "\\\\" +
			"\n{\\it Critical} & " + (tAttr critical) + "\\\\" +
			( subtasks map ( 
				x => {
					val attr = d ? x
					"\n\\hline" + 
					"\n{\\bf Subtask} & " + italics(x.id) + "\\\\" + 
					( if (attr has gist) "\n{\\it Gist} & " + attr.gist + "\\\\" else "") +
					( if (attr has spec) "\n{\\it Spec} & " + attr.spec + "\\\\" else "") +
					( if (attr has problem) "\n{\\it Problem} & " + attr.problem + "\\\\" else "") +
					( if (attr has solution) "\n{\\it Solution} & " + attr.solution + "\\\\" else "")
				}
			)).toList.mkString("\n") + 
			"\n\\end{longtable}\n\\end{framed}"
		}
		def attrSubsection(elms:Map[Attribute,Any]):String = if (elms.isEmpty) ""
			else "\n\\subsection{Attributes}\n\\begin{description}" + 
			{ for ((a,v) <- elms) yield 
				( "\n\\item["  + a + "]: " +  ( if (a != test) v.toString else verbatim(v.toString)) )
			}.mkString + "\n\\end{description}"
		def entitySection(dom:Domain):String = ( for ((k,e) <- dom.db) yield k match { 
			case ek : Ent =>	{
				"\n\\section{" + firstCap(ek.ent.toString) + " " + ek.id  + "}\n" + 
				setLabel(ek) +
				( if (d ? ek has text) "\n" + (d ? ek text) else "") + { 
					e match {
						case av : Attr =>  
						//here it would be nice to match on ek.ent but case sensitivity in match prevents this :-(
							if (ek.ent == product) contextDiagram(ek) + attrSubsection(av.elms)
							else if (ek.ent == userstory) userStoryFrame(ek) + attrSubsection(av.elms - role - desire - benefit)
							else if (ek.ent == task) taskTable(ek) + attrSubsection(av.elms - purpose - trigger - frequency - critical)
							else attrSubsection(av.elms) 
						case _ =>  "ERROR no Attr"
					} 
				} + {
					val myRels = d / ek / Relation
					if (myRels.isEmpty) "" else {
					"\n\\subsection{Relations}\n" + 
						( 
							for ((k,e) <- myRels.db) yield k match {
								case r : Rel => e match {  
									case rt: RelTo => ( for (re <- rt.elms; if (re == ek || r.entKey == ek)) yield 
										(entToLatex(r.entKey) + " " + r.rel  + " " + re.ent + " \\emph{" + refLabel(re, re.id) + "}") 
									).mkString("", ". \\\\ \n", ". \\\\ \n")
									case _ => "ERROR no RelTo"
								}
								case _ =>  "ERROR no Rel"
							} 
						).mkString("", "", "\n")
					}
				}
			}
			case _  => "ERROR no Ent"
		} ).mkString 
		def requirements = entitySection(dReqs)
		
		latexPreamble +
			"\n\\title{" + title +"}" + 
			"\n\\author{" + author + "\\vspace{1cm} \\\\ \\href{http://reqT.org}{reqT.org}" +"}" + 
			"\n\\maketitle" +
			"\n\\tableofcontents" + 
			"\n\\listoffigures" + 
			"\n\\listoftables" + 
			( if (introduction.isEmpty) "" else "\n\\chapter{Introduction to " + name + "}\n"+introduction ) +
			( if (overview.isEmpty) "" else "\n\\chapter{Overview}\n"+overview) +
			( if (contextChapter.isEmpty) "" else "\n\\chapter{Context}\n"+contextChapter ) +
			( if (requirements.isEmpty) "" else "\n\\chapter{Requirements}\n"+requirements ) +
			latexEnding + "\n" +
			{ warn.on; "" }
	}	
}

object warn {
	private var warnMe = true
	def on = {warnMe = true } ; def off = {warnMe = false }
	def apply(w:String) = if (warnMe) println("--- Warning: " + w)
}

object ? { //TODO make this complete
	override def toString = (
	"\n*** reqT is a requirements engineering Tool ***" +
	"\n         For help type ? <obj>" +
	"\n         where <obj> can be:"+
	"\n         Metamodel Concept Domain feature" +
	"\n*************************************************"
	)
	def Metamodel = println(
	"A Metamodel represents the types that are used create requirements models.\n" +
	"Metamodel is root of the type tree in reqT.\n" +
	"The direct Metamodel subtypes are:\n" +
	"Concept, Domain, Attribute, AttributeValue, Key, Elements."
	)
	def Concept = println(
	"A Concept can be part of a Domain.\n" +
    "The direct Concept subtypes are: Entity, Relation"
	)
	def Domain = println(
	"A Domain is an area of application of (software) products.\n"+
	"A Domain has Concepts. \nDomains are created by typing "+
	"Domain(<Entity>,<Entity>, <Entity>, ...), for example:\n" + 
	"var d = Domain(feature(\"myFeatId\", gist -> \"This is a short description.\"))"
	)
	def feature = println(
	"A feature is a releasable characteristic of a Product.\n" + 
	"A feature can have the following attributes:\n" + 
	reqT.feature.attrSeq.mkString(", ") + "\n" +
	"Example: var f = feature(\"myFeatId\", gist -> \"This is a short description.\")"
	)
}

object help { override def toString = "? for help"}

case class Time(date:java.util.Calendar)  { //a wrapper for java java.util.Calendar
	def year:Int = date.get(java.util.Calendar.YEAR)
	def year_=(y:Int) {date.set(java.util.Calendar.YEAR,y)}
	def year(y:Int):Time = {date.set(java.util.Calendar.YEAR,y); this }
	def month:Int = date.get(java.util.Calendar.MONTH)+1
	def month_=(m:Int) {date.set(java.util.Calendar.MONTH,m-1)} // Jan=1, Feb=2 etc
	def month(m:Int):Time = {date.set(java.util.Calendar.MONTH,m-1); this } 
	def day:Int = date.get(java.util.Calendar.DAY_OF_MONTH)
	def day_=(d:Int) {date.set(java.util.Calendar.DAY_OF_MONTH,d)}
	def day(d:Int):Time = {date.set(java.util.Calendar.DAY_OF_MONTH,d); this }
	def hour:Int = date.get(java.util.Calendar.HOUR_OF_DAY)
	def hour_=(h:Int) {date.set(java.util.Calendar.HOUR_OF_DAY, h)}
	def hour(h:Int):Time = {date.set(java.util.Calendar.HOUR_OF_DAY, h); this }
	def minute:Int = date.get(java.util.Calendar.MINUTE)
	def minute_=(m:Int) {date.set(java.util.Calendar.MINUTE,m)}
	def minute(m:Int):Time = {date.set(java.util.Calendar.MINUTE,m); this }
	def second:Int = date.get(java.util.Calendar.SECOND)
	def second_=(s:Int) {date.set(java.util.Calendar.SECOND,s)}
	def second(s:Int):Time = {date.set(java.util.Calendar.SECOND,s); this }
	def millis:Long = date.getTimeInMillis
	def millis_=(ms:Long) {date.setTimeInMillis(ms)}
	def millis(ms:Long):Time = {date.setTimeInMillis(ms); this }
	def addYear(y:Int):Time = { year += y; this }
	def addMonth(m:Int):Time = { month += m; this }
	def addDay(d:Int):Time = { day += d; this } 
	def addHour(h:Int):Time = { hour += h; this }
	def addMinute(m:Int):Time = { minute += m; this }
	def addSecond(s:Int):Time = { second += s; this }
	def week:String = ""+year+"W"+date.get(java.util.Calendar.WEEK_OF_YEAR)
	var epsilon = 1000 //the number +- milliseconds that still counts as equal time
	def >(t:Time):Boolean = millis/epsilon > t.millis/epsilon
	def >=(t:Time):Boolean = millis/epsilon >= t.millis/epsilon
	def <(t:Time):Boolean = millis/epsilon < t.millis/epsilon	
	def <=(t:Time):Boolean = millis/epsilon <= t.millis/epsilon
	def ==(t:Time):Boolean = millis/epsilon == t.millis/epsilon
	def format:String = date.getTime.toString
	override def toString:String = if (year == 1 && month == 1 && day == 1) "Time.noTime" else 
		"Time(y=" + year + ",m=" + month + ",d=" + day + ( 
			if (hour!=0 || minute!=0 || second !=0) ",h=" + hour + ",min=" + minute + ",sec=" + second else "" 
		) + ")"
}
case object Time { 	
	def now = Time()
	def noTime = Time(1,1,1)
	def apply():Time = Time( new java.util.GregorianCalendar) //initialized with today
	def apply(m:Long):Time = Time() millis m
	def apply(y:Int, m:Int, d:Int, h:Int=0, min:Int=0, sec:Int=0):Time = 
		Time() year y month m day d hour h minute min second sec
}